#include "warray/warray.h"

warp::arraynd<float,1> func_returning_nonconst()
{
	return warp::arraynd<float,1>();
}

warp::const_arraynd<float,1> func_returning_const()
{
	return warp::const_arraynd<float,1>();
}

void func_taking_nonconst(warp::arraynd<float,1> a)
{
}

void func_taking_const(warp::const_arraynd<float,1> a)
{
}

void func_taking_nonconst_ref(warp::arraynd<float,1>& a)
{
}

void func_taking_const_ref(warp::const_arraynd<float,1>& a)
{
}

template<size_t N>
void test_indexing(warp::arraynd<float,N>& A, warp::const_arraynd<float,N>& B)
{
}

template<>
void test_indexing<1>(warp::arraynd<float,1>& A, warp::const_arraynd<float,1>& B)
{
	A[0] = 1.0f;
	A[1] = A[0];
	A[2] = B[0];
	A[warp::indices[0]] = B[warp::indices[0]];
}

template<>
void test_indexing<2>(warp::arraynd<float,2>& A, warp::const_arraynd<float,2>& B)
{
	A[0][0] = 1.0f;
	A[1][1] = A[0][0];
	A[2][2] = B[0][0];	
	A[warp::indices[0][0]] = B[warp::indices[0][0]];
	A[warp::indices[0]][0] = B[warp::indices[0]][0];
	A[0][warp::indices[0]] = B[0][warp::indices[0]];
	
	// We should be able to slice!
	warp::arraynd<float,1> Aslice = A.slice(warp::indices[0]);
	warp::const_arraynd<float,1> Bslice = B.slice(warp::indices[0]);
}

template<>
void test_indexing<3>(warp::arraynd<float,3>& A, warp::const_arraynd<float,3>& B)
{
	A[0][0][0] = 1.0f;
	A[1][1][1] = A[0][0][0];
	A[2][2][2] = B[0][0][0];
	A[warp::indices[0][0][0]] = B[warp::indices[0][0][0]];
	A[warp::indices[0][0]][0] = B[warp::indices[0][0]][0];
	A[warp::indices[0]][0][0] = B[warp::indices[0]][0][0];
	A[0][warp::indices[0][0]] = B[0][warp::indices[0][0]];
	
	// We should be able to slice!
	warp::arraynd<float,2> Aslice2d = A.slice(warp::indices[0]);
	warp::const_arraynd<float,2> Bslice2d = B.slice(warp::indices[0]);
	warp::arraynd<float,1> Aslice1d = A.slice(warp::indices[0][0]);
	warp::const_arraynd<float,1> Bslice1d = B.slice(warp::indices[0][0]);
}

template<size_t N>
void test_array(const warp::array_extents<N>& extents)
{
	// Simple construction
	warp::arraynd<float,N> A(extents,0.0f);
	warp::const_arraynd<float,N> B(extents,0.0f);

	// Construction of references
	warp::arraynd<float,N> Aref(A);
	warp::const_arraynd<float,N> constAref(A);
	warp::const_arraynd<float,N> constBref(B);

	// Assignment of references
	Aref = A;
	constAref = A;
	constBref = B;
	
	// Copies
	warp::arraynd<float,N> Acopy = A.copy();
	warp::arraynd<float,N> Bcopy = B.copy(); // You can copy a const array to get a non-const one!
	
	// Thin wrappers of c-style arrays
	float *c_array = new float[extents.product()];
	const float *const_c_array = c_array;
	warp::arraynd<float,N> C(extents,c_array);
	warp::const_arraynd<float,N> constC(extents,c_array);
	warp::const_arraynd<float,N> constCconst(extents,const_c_array);
	delete[] c_array;
	
	// Constant assignment
	A.fill(1.0f);
	
	// Injection
	A.inject(C);
	A.inject(B);
	
	// Indexing
	test_indexing(A,B);
	
	// Reshaping
	A.reshape(extents);
	B.reshape(extents);
	
	// Strips
	A = A.strip(1,2);
	B = B.strip(1,2);
}

int main(int argc, char *argv[])
{
	// Desired array nd behaviour
	test_array(warp::extents[5]);
	test_array(warp::extents[5][5]);
	test_array(warp::extents[5][5][5]);
	
	// Function return values
	warp::arraynd<float,1> F;
	warp::const_arraynd<float,1> constF;
	
	F = func_returning_nonconst();
	constF = func_returning_nonconst();
	constF = func_returning_const();
	 
	// Function arguments
	func_taking_nonconst(F);
	func_taking_const(F);
	func_taking_const(constF);
	 
	func_taking_nonconst_ref(F);
	func_taking_const_ref(constF);
	
	return 0;
}

